package com.yupi.springbootinit.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yupi.springbootinit.model.entity.DeviceInfo;
import com.yupi.springbootinit.model.vo.DeviceHistoryVO;
import com.yupi.springbootinit.model.vo.DeviceOverviewItemVO;
import com.yupi.springbootinit.model.vo.DeviceStatusVO;
import com.yupi.springbootinit.model.vo.HistoryPointVO;
import com.yupi.springbootinit.repository.InfluxRepository;
import com.yupi.springbootinit.service.DeviceDataService;
import com.yupi.springbootinit.service.DeviceInfoService;
import com.yupi.springbootinit.service.StatisticsService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Instant;
import java.util.*;

/**
 * @author WwW
 * @description 设备数据服务实现类，提供设备状态查询、历史数据查询等功能
 */
@Service
@RequiredArgsConstructor
public class DeviceDataServiceImpl implements DeviceDataService {

    private final DeviceInfoService deviceInfoService;
    private final InfluxRepository influxRepository;
    private final StatisticsService statisticsService;

    private static final String DEFAULT_START = null; // 使用仓库的 latestRecentRange
    private static final String DEFAULT_WINDOW = null; // 使用仓库的 aggWindow（4m）
    private static final String DEFAULT_AGG_FN = null; // 使用仓库的 aggFn（mean）

    /**
     * 获取设备概览信息列表
     * @return 设备概览信息列表
     */
    @Override
    public List<DeviceOverviewItemVO> getOverview() {
        List<DeviceInfo> devices = deviceInfoService.list(new LambdaQueryWrapper<>());
        List<DeviceOverviewItemVO> res = new ArrayList<>(devices.size());
        for (DeviceInfo d : devices) {
            DeviceOverviewItemVO vo = new DeviceOverviewItemVO();
            vo.setDeviceId(d.getId());
            vo.setDeviceName(d.getDeviceName());

            List<String> measurements = resolveMeasurements(d);

            // 1) 环境类字段（聚合）
            List<String> aggFields = List.of("oxygen", "cabinTemp");
            Optional<Map<String, Object>> aggOpt = influxRepository.queryLatestAggOnlyByNames(
                    measurements,
                    d.getInfluxTagName(),
                    aggFields,
                    "-24h", // 使用仓库默认 latestRecentRange
                    null, // 使用仓库默认 aggWindow(4m)
                    null  // 使用仓库默认 aggFn(mean)
            );

            // 2) 状态字段（原始 last）
            List<String> lastFields = List.of("statusCode", "machineStatus");
            Optional<Map<String, Object>> lastOpt = influxRepository.queryLatestRawOnlyByNames(
                    measurements,
                    d.getInfluxTagName(),
                    lastFields,
                    null, // 使用默认近窗口
                    null  // 使用默认回退窗口
            );

            // 3) 合并
            Map<String, Object> row = new HashMap<>();
            aggOpt.ifPresent(row::putAll);
            lastOpt.ifPresent(row::putAll);

            if (!row.isEmpty()) {
                vo.setTime(toIsoTime(row.get("time")));
                vo.setStatus(resolveMachineStatus(row, d));
                vo.setOxygen(toDouble(row.get("oxygen")));
                vo.setCabinTemp(toDouble(row.get("cabinTemp")));

                // 概览中 oxygen 保留三位小数
                vo.setOxygen(round(vo.getOxygen(), 3));
            }

            res.add(vo);
        }
        return res;
    }

    /**
     * 获取设备最新状态信息
     * @param deviceId 设备ID
     * @return 设备状态VO对象
     * @throws IllegalArgumentException 当设备不存在时抛出异常
     */
    @Override
    public DeviceStatusVO getLatestStatus(Integer deviceId) {
        DeviceInfo device = deviceInfoService.getById(deviceId);
        if (device == null) {
            throw new IllegalArgumentException("设备不存在: id=" + deviceId);
        }

        DeviceStatusVO vo = new DeviceStatusVO();
        List<String> measurements = resolveMeasurements(device);

        // 1) 环境类字段（聚合，允许 fallback）
        List<String> aggFields = List.of("oxygen", "cabinTemp", "filterDp", "ductTemp");
        Optional<Map<String, Object>> envOpt = influxRepository.queryLatestAggOnlyByNames(
                measurements,
                device.getInfluxTagName(),
                aggFields,
                "-24h",
                null,
                null
        );

        // 2) 状态 / 任务类字段（原始 last，禁用 fallback，避免取到很久以前的旧状态）
        List<String> lastFields = List.of(
                "statusCode", "machineStatus",
                "totalAccount", "remainingAccount",
                "totalHeight", "remainingHeight",
                "estimatedTime", "remainingTime",
                "printStart", "printOver",
                "currentLayer", "layerThickness"
        );
        Optional<Map<String, Object>> statusOpt = influxRepository.queryLatestRawOnlyByNames(
                measurements,
                device.getInfluxTagName(),
                lastFields,
                null,   // recent: 使用默认近窗口 latestRecentRange（-30m）
                "0s"    // fallback: 禁用（不从 -30d 兜底旧值）
        );

        // 3) 合并环境与状态
        Map<String, Object> row = new HashMap<>();
        envOpt.ifPresent(row::putAll);
        statusOpt.ifPresent(row::putAll);

        // 4) 计算 time：取两次查询中较新的时间
        String latestIso = null;
        try {
            Instant tEnv = envOpt.map(m -> m.get("time")).map(Object::toString).map(java.time.Instant::parse).orElse(null);
            Instant tStat = statusOpt.map(m -> m.get("time")).map(Object::toString).map(java.time.Instant::parse).orElse(null);
            Instant latest = (tEnv == null) ? tStat : (tStat == null ? tEnv : (tEnv.isAfter(tStat) ? tEnv : tStat));
            if (latest != null) latestIso = latest.toString();
        } catch (Exception ignore) {
        }

        if (!row.isEmpty()) {
            vo.setTime(latestIso != null ? latestIso : toIsoTime(row.get("time")));
            mergeRow(vo, row);
            vo.setMachineStatus(resolveMachineStatus(row, device));

            // MQTT 设备：printStart 秒级时间戳 → startTime + printStart=true
            if ("MQTT".equalsIgnoreCase(device.getProtocolType())) {
                Long ps = toLong(row.get("printStart"));
                if (ps != null && ps > 0) {
                    vo.setStartTime(epochSecToIso(ps));
                    vo.setPrintStart(true);
                }
                if (vo.getRemainingTime() != null) {
                    vo.setPrintOver(vo.getRemainingTime() <= 0.0);
                }
            }

            // 补推导：remainingAccount/高度等
            Integer total = vo.getTotalAccount();
            Integer currentLayer = toInteger(row.get("currentLayer"));
            Double thickness = toDouble(row.get("layerThickness"));
            if (vo.getRemainingAccount() == null && total != null && currentLayer != null) {
                vo.setRemainingAccount(Math.max(0, total - currentLayer));
            }
            if (vo.getTotalHeight() == null && thickness != null && total != null) {
                vo.setTotalHeight(thickness * total);
            }
            if (vo.getRemainingHeight() == null && thickness != null && vo.getRemainingAccount() != null) {
                vo.setRemainingHeight(thickness * vo.getRemainingAccount());
            }
        }

        // 状态缺失（因禁用 fallback 可能为 null）→ 统一按待机
        if (vo.getMachineStatus() == null) {
            vo.setMachineStatus(0);
        }

        // 计算进度 / 开始时间等
        vo = enrichStatus(vo, device);

        // 待机态清理任务字段，避免历史残留污染
        sanitizeWhenIdle(vo);

        // 数字格式化
        applyNumberFormat(vo);

        return vo;
    }

    /**
     * 获取设备历史数据
     * @param deviceId 设备ID
     * @param fieldsCsv 字段列表（CSV格式）
     * @param startArg 开始时间参数
     * @param aggArg 聚合参数
     * @return 设备历史数据VO对象
     * @throws IllegalArgumentException 当设备不存在时抛出异常
     */
    @Override
    public DeviceHistoryVO getHistory(Integer deviceId, String fieldsCsv, String startArg, String aggArg) {
        DeviceInfo d = deviceInfoService.getById(deviceId);
        if (d == null) throw new IllegalArgumentException("设备不存在");

        List<String> measurements = resolveMeasurements(d);
        String tag = d.getInfluxTagName();

        List<String> fields = Arrays.stream(fieldsCsv.split(","))
                .map(String::trim).filter(s -> !s.isEmpty()).toList();

        List<InfluxRepository.HistoryPoint> points;
        if (aggArg == null || aggArg.isBlank() || "raw".equalsIgnoreCase(aggArg)) {
            points = influxRepository.queryHistoryRawByNames(measurements, tag, fields, startArg);
        } else {
            points = influxRepository.queryHistoryAggByNames(measurements, tag, fields, startArg, aggArg);
        }

        Map<String, List<HistoryPointVO>> series = new HashMap<>();
        for (InfluxRepository.HistoryPoint p : points) {
            if (p.field == null) continue;
            HistoryPointVO hp = new HistoryPointVO(p.time, p.value);
            series.computeIfAbsent(p.field, k -> new ArrayList<>()).add(hp);
        }
        series.values().forEach(list -> list.sort(Comparator.comparing(HistoryPointVO::getTime)));

        DeviceHistoryVO vo = new DeviceHistoryVO();
        vo.setSeries(series);
        return vo;
    }

    /**
     * 丰富设备状态信息，包括计算进度等
     * @param vo 设备状态VO对象
     * @param device 设备信息实体
     * @return 丰富后的设备状态VO对象
     */
    @Override
    public DeviceStatusVO enrichStatus(DeviceStatusVO vo, DeviceInfo device) {
        // 计算进度（clamp 到 0~100，并统一保留两位小数）
        double p = statisticsService.calcProgress(vo);
        if (Double.isNaN(p) || Double.isInfinite(p)) {
            p = 0.0;
        } else {
            if (p < 0) p = 0;
            if (p > 100) p = 100;
        }
        vo.setProgress(round(p, 2));

        if (vo.getStartTime() == null && "MODBUS".equalsIgnoreCase(device.getProtocolType())) {
            String startTime = influxRepository.queryFirstPrintStartTime(
                    resolveMeasurements(device), device.getInfluxTagName(), "-30d");
            vo.setStartTime(startTime);
        }
        return vo;
    }

    /**
     * 待机态清理：当 machineStatus=0 时，认为无任务，清空任务相关字段，避免用回退窗口的历史值污染当前展示
     */
    private void sanitizeWhenIdle(DeviceStatusVO vo) {
        Integer ms = vo.getMachineStatus();
        if (ms != null && ms == 0) {
            vo.setPrintStart(false);
            vo.setPrintOver(false);

            vo.setEstimatedTime(null);
            vo.setRemainingTime(null);

            vo.setTotalAccount(null);
            vo.setRemainingAccount(null);

            vo.setTotalHeight(null);
            vo.setRemainingHeight(null);

            // 待机进度置 0
            vo.setProgress(0.0);
        }
    }

    /**
     * 解析设备测量信息列表
     * @param device 设备信息实体
     * @return 测量信息列表
     */
    private List<String> resolveMeasurements(DeviceInfo device) {
        List<String> ms = device.getInfluxMeasurementList();
        if (ms == null || ms.isEmpty()) {
            ms = List.of("MODBUS".equalsIgnoreCase(device.getProtocolType()) ? "modbus_data" : "device_data_mqtt");
        }
        // 替换非法字符，保持与 Influx measurement 一致
        return ms.stream().map(s -> s.replace('-', '_')).toList();
    }

    /**
     * 填充PLC/Modbus设备的所有字段信息
     * @param row 数据行
     * @param vo 设备状态VO对象
     */
    private void fillAllFields(Map<String, Object> row, DeviceStatusVO vo) {
        vo.setTime(toIsoTime(row.get("time")));
        vo.setOxygen(toDouble(row.get("oxygen")));
        vo.setCabinTemp(toDouble(row.get("cabinTemp")));
        vo.setFilterDp(toDouble(row.get("filterDp")));
        vo.setDuctTemp(toDouble(row.get("ductTemp")));
        vo.setTotalAccount(toInteger(row.get("totalAccount")));
        vo.setRemainingAccount(toInteger(row.get("remainingAccount")));
        vo.setTotalHeight(toDouble(row.get("totalHeight")));
        vo.setRemainingHeight(toDouble(row.get("remainingHeight")));
        vo.setEstimatedTime(toDouble(row.get("estimatedTime")));
        vo.setRemainingTime(toDouble(row.get("remainingTime")));
        vo.setMachineStatus(toInteger(row.get("machineStatus")));
        vo.setPrintStart(toBoolean(row.get("printStart")));
        vo.setPrintOver(toBoolean(row.get("printOver")));
    }

    /**
     * 填充MQTT Atmosphere设备的字段信息
     * @param row 数据行
     * @param vo 设备状态VO对象
     */
    private void fillAtmosphereAndTaskFields(Map<String, Object> row, DeviceStatusVO vo) {
        vo.setTime(toIsoTime(row.get("time")));
        vo.setOxygen(toDouble(row.get("oxygen")));
        vo.setCabinTemp(toDouble(row.get("cabinTemp")));
        vo.setFilterDp(toDouble(row.get("filterDp")));
        vo.setDuctTemp(toDouble(row.get("ductTemp")));
        vo.setTotalAccount(toInteger(row.get("totalAccount")));
        vo.setRemainingAccount(toInteger(row.get("remainingAccount")));
        vo.setTotalHeight(toDouble(row.get("totalHeight")));
        vo.setRemainingHeight(toDouble(row.get("remainingHeight")));
        vo.setEstimatedTime(toDouble(row.get("estimatedTime")));
        vo.setRemainingTime(toDouble(row.get("remainingTime")));
        vo.setPrintStart(toBoolean(row.get("printStart")));
        vo.setPrintOver(toBoolean(row.get("printOver")));
    }

    /**
     * 将对象转换为Integer类型
     * @param o 待转换对象
     * @return 转换后的Integer值
     */
    private static Integer toInteger(Object o) {
        if (o == null) return null;
        if (o instanceof Number) return ((Number) o).intValue();
        try { return Double.valueOf(o.toString()).intValue(); } catch (Exception e) { return null; }
    }
    /**
     * 将对象转换为Double类型
     * @param o 待转换对象
     * @return 转换后的Double值
     */
    private static Double toDouble(Object o) {
        if (o == null) return null;
        if (o instanceof Number) return ((Number) o).doubleValue();
        try { return Double.valueOf(o.toString()); } catch (Exception e) { return null; }
    }
    /**
     * 将对象转换为Boolean类型
     * @param o 待转换对象
     * @return 转换后的Boolean值
     */
    private static Boolean toBoolean(Object o) {
        if (o == null) return null;
        if (o instanceof Boolean) return (Boolean) o;
        if (o instanceof Number) return ((Number) o).intValue() != 0;
        String s = o.toString().trim();
        if ("1".equals(s)) return true;
        if ("0".equals(s)) return false;
        return Boolean.parseBoolean(s);
    }
    /**
     * 将对象转换为ISO时间格式字符串
     * @param o 待转换对象
     * @return 转换后的时间字符串
     */
    private static String toIsoTime(Object o) { return o == null ? null : o.toString(); }
    /**
     * 填充任务相关字段
     * @param row 数据行
     * @param vo 设备状态VO对象
     */
    private void fillTaskFields(Map<String, Object> row, DeviceStatusVO vo) {
        vo.setTotalAccount(toInteger(row.get("totalAccount")));
        vo.setRemainingAccount(
                vo.getTotalAccount() != null && row.get("currentLayer") != null
                        ? vo.getTotalAccount() - toInteger(row.get("currentLayer"))
                        : null
        );
        vo.setTotalHeight(toDouble(row.get("layerThickness")) * toInteger(row.get("totalAccount")));
        vo.setRemainingHeight(
                toDouble(row.get("layerThickness")) * toInteger(vo.getRemainingAccount())
        );
        vo.setEstimatedTime(toDouble(row.get("remainingTime"))); // 或者另有字段
        vo.setRemainingTime(toDouble(row.get("remainingTime")));
    }
    /**
     * 通用字段合并方法，将数据行中的字段映射到DeviceStatusVO对象中
     * @param vo 设备状态VO对象
     * @param row 数据行
     */
    private void mergeRow(DeviceStatusVO vo, Map<String,Object> row) {
        vo.setOxygen(toDouble(row.get("oxygen")));
        vo.setCabinTemp(toDouble(row.get("cabinTemp")));
        vo.setFilterDp(toDouble(row.get("filterDp")));
        vo.setDuctTemp(toDouble(row.get("ductTemp")));
        vo.setTotalAccount(toInteger(row.get("totalAccount")));
        vo.setRemainingAccount(toInteger(row.get("remainingAccount")));

        vo.setTotalHeight(toDouble(row.get("totalHeight")));
        vo.setRemainingHeight(toDouble(row.get("remainingHeight")));

        vo.setEstimatedTime(toDouble(row.get("estimatedTime")));
        vo.setRemainingTime(toDouble(row.get("remainingTime")));
        vo.setMachineStatus(firstInt(row, "statusCode", "machineStatus"));

    }
    /**
     * 解析设备测量信息
     * @param device 设备信息实体
     * @return 测量信息字符串
     */
    private String resolveMeasurement(DeviceInfo device) {
        String m = device.getInfluxMeasurements();
        if (m == null || m.isBlank()) {
            return "MODBUS".equalsIgnoreCase(device.getProtocolType()) ? "modbus_data" : "device_data_mqtt";
        }
        return m.replace('-', '_');
    }

    /**
     * 将对象转换为Long类型
     * @param o 待转换对象
     * @return 转换后的Long值
     */
    private static Long toLong(Object o) {
        if (o == null) return null;
        if (o instanceof Number) return ((Number) o).longValue();
        try { return Long.parseLong(String.valueOf(o)); } catch (Exception e) { return null; }
    }
    /**
     * 将时间戳转换为ISO时间格式字符串
     * @param sec 时间戳（秒）
     * @return ISO时间格式字符串
     */
    private static String epochSecToIso(Long sec) {
        if (sec == null) return null;
        return java.time.Instant.ofEpochSecond(sec).toString();
    }
    /**
     * 从数据行中获取第一个非空整数值
     * @param row 数据行
     * @param keys 键名数组
     * @return 第一个非空整数值
     */
    private static Integer firstInt(Map<String,Object> row, String... keys) {
        for (String k : keys) {
            Integer v = toInteger(row.get(k));
            if (v != null) return v;
        }
        return null;
    }

    private static Double round(Double v, int scale) {
        if (v == null) return null;
        return BigDecimal.valueOf(v).setScale(scale, RoundingMode.HALF_UP).doubleValue();
    }

    private static void applyNumberFormat(DeviceStatusVO vo) {
        vo.setOxygen(round(vo.getOxygen(), 3));
        vo.setTotalHeight(round(vo.getTotalHeight(), 2));
        vo.setRemainingHeight(round(vo.getRemainingHeight(), 2));
    }

    /**
     * 统一状态解析：
     * 1) 优先使用 statusCode（Telegraf 已归一化）
     * 2) 若无 statusCode，且协议为 MQTT，则按厂家 machineStatus → 归一化映射
     * 3) 再无则用任务字段推断，最后兜底 0（待机）
     * <p>
     * 归一化目标：0=待机/空闲，1=运行/忙碌，2=异常/暂停
     */
    private Integer resolveMachineStatus(Map<String, Object> row, DeviceInfo device) {
        // 1) 优先 statusCode
        Integer code = toInteger(row.get("statusCode"));
        if (code != null) return code;

        // 2) MQTT 设备：用原始 machineStatus 做后端映射（与 telegraf processors.enum 一致）
        Integer ms = toInteger(row.get("machineStatus"));
        if (ms != null) {
            if ("MQTT".equalsIgnoreCase(device.getProtocolType())) {
                // telegraf 中的映射：
                // 0=0(不可用)，1=0(未启动)，2=0(空闲)，3=1(准备)，4=1(打印中)，5=2(暂停)，6=2(报警)，7=1(忙碌)
                switch (ms) {
                    case 0:
                    case 1:
                    case 2:
                    case 7:
                        return 0;
                    case 3:
                    case 4:
                        return 1;
                    case 5:
                    case 6:
                        return 2;
                    default:
                        break;
                }
            } else {
                // 非 MQTT（如 MODBUS）：通常已是 0/1/2；3(忙碌)按运行处理
                if (ms == 3) return 1;
                if (ms >= 0 && ms <= 2) return ms;
            }
        }

        // 3) 任务字段推断：remainingTime>0 → 运行；<=0 → 待机；printStart/printOver 辅助判断
        Double rt = toDouble(row.get("remainingTime"));
        if (rt != null) return rt > 0 ? 1 : 0;

        Boolean ps = toBoolean(row.get("printStart"));
        if (ps != null) return ps ? 1 : 0;

        Boolean po = toBoolean(row.get("printOver"));
        if (po != null) return po ? 0 : 1;

        // 4) 兜底：待机
        return 0;
    }
}